package com.hqd.ch03.v35.web.ui;

import com.hqd.ch03.v35.core.Conventions;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentModel extends ConcurrentHashMap<String, Object> implements Model {

    /**
     * Construct a new, empty {@code ConcurrentModel}.
     */
    public ConcurrentModel() {
    }

    /**
     * Construct a new {@code ModelMap} containing the supplied attribute
     * under the supplied name.
     *
     * @see #addAttribute(String, Object)
     */
    public ConcurrentModel(String attributeName, Object attributeValue) {
        addAttribute(attributeName, attributeValue);
    }

    /**
     * Construct a new {@code ModelMap} containing the supplied attribute.
     * Uses attribute name generation to generate the key for the supplied model
     * object.
     *
     * @see #addAttribute(Object)
     */
    public ConcurrentModel(Object attributeValue) {
        addAttribute(attributeValue);
    }
    
    @Override
    public Object put(String key, Object value) {
        if (value != null) {
            return super.put(key, value);
        } else {
            return remove(key);
        }
    }

    @Override
    public void putAll(Map<? extends String, ?> map) {
        for (Map.Entry<? extends String, ?> entry : map.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    /**
     * Add the supplied attribute under the supplied name.
     *
     * @param attributeName  the name of the model attribute (never {@code null})
     * @param attributeValue the model attribute value (ignored if {@code null},
     *                       just removing an existing entry if any)
     */
    @Override
    public ConcurrentModel addAttribute(String attributeName, Object attributeValue) {
        put(attributeName, attributeValue);
        return this;
    }

    @Override
    public ConcurrentModel addAttribute(Object attributeValue) {
        if (attributeValue instanceof Collection && ((Collection<?>) attributeValue).isEmpty()) {
            return this;
        }
        return addAttribute(Conventions.getVariableName(attributeValue), attributeValue);
    }

    /**
     * Copy all attributes in the supplied {@code Collection} into this
     * {@code Map}, using attribute name generation for each element.
     *
     * @see #addAttribute(Object)
     */
    @Override
    public ConcurrentModel addAllAttributes(Collection<?> attributeValues) {
        if (attributeValues != null) {
            for (Object attributeValue : attributeValues) {
                addAttribute(attributeValue);
            }
        }
        return this;
    }

    /**
     * Copy all attributes in the supplied {@code Map} into this {@code Map}.
     *
     * @see #addAttribute(String, Object)
     */
    @Override
    public ConcurrentModel addAllAttributes(Map<String, ?> attributes) {
        if (attributes != null) {
            putAll(attributes);
        }
        return this;
    }

    /**
     * Copy all attributes in the supplied {@code Map} into this {@code Map},
     * with existing objects of the same name taking precedence (i.e. not getting
     * replaced).
     */
    @Override
    public ConcurrentModel mergeAttributes(Map<String, ?> attributes) {
        if (attributes != null) {
            attributes.forEach((key, value) -> {
                if (!containsKey(key)) {
                    put(key, value);
                }
            });
        }
        return this;
    }

    /**
     * Does this model contain an attribute of the given name?
     *
     * @param attributeName the name of the model attribute (never {@code null})
     * @return whether this model contains a corresponding attribute
     */
    @Override
    public boolean containsAttribute(String attributeName) {
        return containsKey(attributeName);
    }

    @Override
    public Object getAttribute(String attributeName) {
        return get(attributeName);
    }

    @Override
    public Map<String, Object> asMap() {
        return this;
    }

}
